package cave.game;

import java.util.ArrayList;
import java.util.List;

import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceView20;

import cave.game.InputEvent.Type;
import cave.renderer.Renderer;
import cave.utils.Pool;
import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;

public class MainActivity extends Activity implements SensorEventListener {
	
	public static String TAG = "Cave";
	public static String PACKAGE_NAME;
	public static Context CONTEXT;
	
	public static InputEvent.Type[] actionMap = {Type.DOWN, Type.UP, Type.MOVE, Type.MOVE, 
		Type.MOVE, Type.DOWN, Type.UP};
	
	private GameRoot gameRoot;
	private Renderer renderer;
	
	private GLSurfaceView20 surfaceView;
	
	private Object inputEventLock;
	private ArrayList<InputEvent> inputEvents;
	private Object activityEventLock;
	private ArrayList<ActivityEvent> activityEvents;
	private volatile boolean waitForGameThread;
	private volatile boolean gameThreadRunning;
	
	private Sensor magneticSensor = null, acceleSensor = null;
	private float[] magneticVec = new float[3];
	private float[] acceleVec = new float[3];
	private float[] rotMatrix = new float[9];
	private float[] orientation = new float[3];
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        PACKAGE_NAME = getApplicationContext().getPackageName();
        CONTEXT = getBaseContext();
        
        inputEventLock = new Object();
        inputEvents = new ArrayList<InputEvent>();
        activityEvents = new ArrayList<ActivityEvent>();
        activityEventLock = new Object();
        waitForGameThread = true;
              
        registerSensors();
        
        // Init screen info
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        Screen.setValues(metrics.widthPixels, metrics.heightPixels);
        
        renderer = new Renderer();
        surfaceView = new GLSurfaceView20(this);
        surfaceView.setRenderer(renderer);
        surfaceView.setRenderMode(GLSurfaceView20.RENDERMODE_WHEN_DIRTY);
        
        spawnGameThread();
        
        setContentView(surfaceView);

    }
    
    private void registerSensors() {
        SensorManager sm = (SensorManager)getSystemService(SENSOR_SERVICE);
        List<Sensor> sensors = sm.getSensorList(Sensor.TYPE_ACCELEROMETER);
        if (sensors.size() == 0) {
        	Log.e(TAG, "MainActivity: accelerometer not found");
        } else {
	        acceleSensor = sensors.get(0);
	        sm.registerListener(this, acceleSensor, SensorManager.SENSOR_DELAY_GAME); 
        }
        
        sensors = sm.getSensorList(Sensor.TYPE_MAGNETIC_FIELD);
        if (sensors.size() == 0) {
        	Log.e(TAG, "MainActivity: magnetic field sensor not found");
        } else {
	        magneticSensor = sensors.get(0);
	        sm.registerListener(this, magneticSensor, SensorManager.SENSOR_DELAY_GAME);
        }
    }
    
    private void spawnGameThread() {
        new Thread(new Runnable() {
        	
        	private ArrayList<InputEvent> inputEventsCopy = new ArrayList<InputEvent>();
        	private ArrayList<ActivityEvent> activityEventsCopy = new ArrayList<ActivityEvent>();
        	
			@Override
			public void run() {
				gameThreadRunning = true;
				gameRoot = new GameRoot(renderer, MainActivity.this);
				
				boolean continueGame = true;
				while (continueGame) {
					boolean pauseEventReceived = false;
					
					synchronized (inputEventLock) {
						int numEvents = inputEvents.size();
						for (int i = 0; i < numEvents; ++i) {
							inputEventsCopy.add(inputEvents.get(i));
						}
						inputEvents.clear();
					}
					synchronized (activityEventLock) {
						int numEvents = activityEvents.size();
						for (int i = 0; i < numEvents; ++i) {
							ActivityEvent event = activityEvents.get(i);
							activityEventsCopy.add(event);
							if (event == ActivityEvent.PAUSE) {
								pauseEventReceived = true;
							}
						}
						activityEvents.clear();
					}
					
					SensorManager.getRotationMatrix(rotMatrix, null, acceleVec, magneticVec);
					SensorManager.getOrientation(rotMatrix, orientation);
					
					continueGame = gameRoot.update(inputEventsCopy, activityEventsCopy, orientation);
					if (pauseEventReceived) {
						waitForGameThread = false;
					}
					
					MainActivity.this.surfaceView.requestRender();
					
					synchronized (inputEventLock) {
						int numEvents = inputEventsCopy.size();
						for (int i = 0; i < numEvents; ++i) {
							Pool.free(inputEventsCopy.get(i));
						}
						inputEventsCopy.clear();
					}
					
					synchronized (activityEventLock) {
						activityEventsCopy.clear();
					}
				}

				gameThreadRunning = false;
				MainActivity.this.finish();
			}
		}).start();  
    }

	@Override
	protected void onPause() {
		super.onPause();
		surfaceView.onPause();
		synchronized (activityEventLock) {
        	activityEvents.add(ActivityEvent.PAUSE);
        }
		
		while(gameThreadRunning && waitForGameThread){
        	Thread.yield();
        }
		waitForGameThread = true;
	}

	@Override
	protected void onResume() {
		super.onResume();
		surfaceView.onResume();
		synchronized (activityEventLock) {
        	activityEvents.add(ActivityEvent.RESUME);
        }
	}
	
	@Override
    protected void onStop() {
        super.onStop();
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
    
	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		
		int action = event.getAction() & MotionEvent.ACTION_MASK;
		
		if (action == MotionEvent.ACTION_MOVE || action == MotionEvent.ACTION_DOWN ||
				action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_DOWN || 
				action == MotionEvent.ACTION_POINTER_UP) {
			
			int mainIndex = (event.getAction() >> MotionEvent.ACTION_POINTER_INDEX_SHIFT);
			
			for (int i = 0; i < event.getPointerCount(); ++i) {
				int id = event.getPointerId(i);
				InputEvent.Type type;
				
				if (i == mainIndex) {
					type = actionMap[action];
				} else {
					type = Type.MOVE;
				}

				float x = event.getX(i) / Screen.getHeight();
				float y = (Screen.getHeight() - event.getY(i)) / Screen.getHeight();
				
				synchronized (inputEventLock) {
					InputEvent inputEvent = Pool.getInputEvent();
					inputEvent.init(type, id, x, y);
					inputEvents.add(inputEvent);
				}
			}
		}
		
		try {
			Thread.sleep(16);
		} catch (InterruptedException e) { }
		
		return true;
	}
	
	@Override
	public boolean dispatchKeyEvent(KeyEvent event) {
		if (event.getDownTime() != event.getEventTime()) {
			synchronized (inputEventLock) {	
				switch (event.getKeyCode()) {
					case KeyEvent.KEYCODE_MENU: {
						InputEvent e = Pool.getInputEvent();
						e.init(InputEvent.Type.MENU, 0, 0, 0);
						inputEvents.add(e);
						break;
					}
					case KeyEvent.KEYCODE_BACK: {
						InputEvent e = Pool.getInputEvent();
						e.init(InputEvent.Type.BACK, 0, 0, 0);
						inputEvents.add(e);
						break;
					}
				}
			}
		}
		return true;
	}

	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {
	}

	@Override
	public void onSensorChanged(SensorEvent event) {
		
		if (event.sensor == magneticSensor) {
			magneticVec[0] = event.values[0];
			magneticVec[1] = event.values[1];
			magneticVec[2] = event.values[2];
		} else {
			acceleVec[0] = event.values[0];
			acceleVec[1] = event.values[1];
			acceleVec[2] = event.values[2];		
		}
		
		//Log.d(TAG, "orientation, x: " + orientation[0] + ", y: " + orientation[1] + ", z: " + orientation[2]);
		
	}
    
	
	
}